// 网络字节序（大端）
export class Byte {
  private data: DataView;
  private ubytes: Uint8Array;
  private pos = 0;
  private backPosStack: {idx: number, poss: number[] } = { idx: 0, poss: [] };

  constructor(private buffer: ArrayBuffer) {
    this.data = new DataView(buffer);
    this.ubytes = new Uint8Array(buffer);
  }

  get NewBuffer() { return new Uint8Array(this.buffer, 0, this.pos); }
  get UBytes() { return this.ubytes; }
  get Buffer() { return this.buffer; }
  get Pos() { return this.pos; }
  set Pos(pos: number) { this.pos = pos; }

  readInt8(): number {
    return this.data.getInt8(this.pos++);
  }

  readInt16(): number {
    return this.data.getInt16((this.pos += 2) - 2);
  }

  readInt32(): number {
    return this.data.getInt32((this.pos += 4) - 4);
  }

  readInt64(): number {
    return Number(this.data.getBigInt64((this.pos += 8) - 8, true)); // 现在服务器传来的longlong是小端
  }

  readUint8(): number {
    return this.ubytes[this.pos++];
  }

  readUint16(): number {
    return this.data.getUint16((this.pos += 2) - 2);
  }

  readUint32(): number {
    return this.data.getUint32((this.pos += 4) - 4);
  }

  readUTF8String(len = 0): string {
    len = len || this.readUint32();
    let strs: string[] = [], f = String.fromCharCode, u = this.ubytes, pos = this.pos;
    let max = pos + len, c = 0, c2 = 0, c3 = 0, i = 0, n = 0, code = 0, offset = 0, lead = 0, trail = 0;
    while (pos <= max) {
      c = u[pos++];
      if (c < 0x80) {
        if (c != 0)
          strs[n++] = f(c);
        else
          break;
      } else if (c < 0xE0) {
        strs[n++] = f(((c & 0x3F) << 6) | (u[pos++] & 0x7F));
      } else if (c < 0xF0) {
        c2 = u[pos++];
        strs[n++] = f(((c & 0x1F) << 12) | ((c2 & 0x7F) << 6) | (u[pos++] & 0x7F));
      } else {
        c2 = u[pos++];
        c3 = u[pos++];
        code = ((c & 0x0F) << 18) | ((c2 & 0x7F) << 12) | ((c3 & 0x7F) << 6) | (u[pos++] & 0x7F);
        if (code >= 0x10000) {
          offset = code - 0x10000;
          lead = 0xd800 | (offset >> 10);
          trail = 0xdc00 | (offset & 0x3ff);
          strs[n++] = f(lead);
          strs[n++] = f(trail);
        }
        else {
          strs[n++] = f(code);
        }
      }
      i++;
    }
    this.pos = pos;
    return strs.join("");
  }

  writeInt8(v: number) {
    this.data.setInt8(this.pos++, v);
  }

  writeInt16(v: number) {
    this.data.setInt16((this.pos += 2) - 2, v);
  }

  writeInt32(v: number) {
    this.data.setInt32((this.pos += 4) - 4, v);
  }

  writeInt64(v: number) {
    this.data.setBigInt64((this.pos += 8) - 8, BigInt(v), true); // 现在服务器传来的longlong是小端
  }

  writeUint8(v: number) {
    this.data.setUint8(this.pos++, v);
  }

  writeUint16(v: number) {
    this.data.setUint16((this.pos += 2) - 2, v);
  }

  writeUint32(v: number) {
    this.data.setUint32((this.pos += 4) - 4, v);
  }

  writeUTF8String(str: string, withlen = true, withTerminateNull = true): number {
    str = str || "";
    let spos = this.pos;
    if (withlen) {
      this.pos += 4; // 跳过长度部分
    }

    let spos2 = this.pos, pos = spos2, u = this.ubytes, c = 0, c2 = 0, p1 = 0, p2 = 0;

    for (let i = 0, sz = str.length; i < sz; i++) {
      c = str.charCodeAt(i);
      if (c <= 0x7F) {
        u[pos++] = c;
      } else if (c <= 0x7FF) {
        u[pos++] = 0xC0 | (c >> 6);
        u[pos++] = 0x80 | (c & 0x3F);
      } else if (c >= 0xD800 && c <= 0xDBFF) {
        i++;
        c2 = str.charCodeAt(i);
        if (!Number.isNaN(c2) && c2 >= 0xDC00 && c2 <= 0xDFFF) {
          p1 = (c & 0x3FF) + 0x40;
          p2 = c2 & 0x3FF;
          u[pos++] = 0xF0 | ((p1 >> 8) & 0x3F);
          u[pos++] = 0x80 | ((p1 >> 2) & 0x3F);
          u[pos++] = 0x80 | ((p1 & 0x3) << 4) | ((p2 >> 6) & 0xF);
          u[pos++] = 0x80 | (p2 & 0x3F);
        }
      } else if (c <= 0xFFFF) {
        u[pos++] = 0xE0 | (c >> 12);
        u[pos++] = 0x80 | ((c >> 6) & 0x3F);
        u[pos++] = 0x80 | (c & 0x3F);
      } else {
        u[pos++] = 0xF0 | (c >> 18);
        u[pos++] = 0x80 | ((c >> 12) & 0x3F);
        u[pos++] = 0x80 | ((c >> 6) & 0x3F);
        u[pos++] = 0x80 | (c & 0x3F);
      }
    }
    if (withTerminateNull) {
      u[pos++] = 0;
    }
    let bytesLen = pos - spos2;
    if (withlen) {
      this.pos = spos;
      this.writeUint32(bytesLen); // 回填长度
    }
    this.pos = pos;
    return pos - spos2;
  }

  writeBytes(data: ArrayBuffer, offset: number, size: number) {
    this.ubytes.set(new Uint8Array(data, offset, size), (this.pos += size) - size);
  }

  skip(bytes: number) {
    this.pos += bytes;
  }

  resetPos(pos: number) {
    this.backPosStack.poss[this.backPosStack.idx++] = this.pos;
    this.pos = pos;
  }

  restorePos() {
    this.pos = this.backPosStack.poss[--this.backPosStack.idx];
  }

  get length(): number {
    return this.pos;
  }
}